/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 * */

// AmzServer - Tototoy's server model.
// Author: tonyjobmails@gmail.com (tonyhack).
//
// Define class UDPCrediblePatternReceiver.
//
// UDPCrediblePatternReceiver is a simple pattern witch achieves acknowledgement
// packet just like TCP, and this pattern can ensure the packet is ordered.
//

#include <string.h>

#include "udp_credible_pattern_receiver.h"
#include "packet_define.h"

namespace amz {

UDPCrediblePatternReceiver::UDPCrediblePatternReceiver(uint32 max_serial_number)
       : serial_number_pool_(max_serial_number), sort_queue_(packet::kMTU_UDP) {}

UDPCrediblePatternReceiver::~UDPCrediblePatternReceiver() {}

uint32 UDPCrediblePatternReceiver::PickSerialNumber(const char *udp_packet) {
  return GET_CREDIBLE_UDP_SERIAL_NUMBER(*(uint32 *)udp_packet);
}

int UDPCrediblePatternReceiver::PacketAcknowledge(bool even_flag, uint32 serial_number,
       char *packet, int max_size) {
  if((uint32)max_size < packet::kCredibleUDPHeadSize) {
    return 0;
  }
  uint32 *head = (uint32 *)packet;
  *head = serial_number;
  SET_CREDIBLE_UDP_ACK(*head);
  if(even_flag)
    SET_CREDIBLE_UDP_EVEN(*head);
  else
    SET_CREDIBLE_UDP_ODD(*head);
  return packet::kCredibleUDPHeadSize;
}

bool UDPCrediblePatternReceiver::CheckSerialNumberOrder(uint32 serial_number) {
  return serial_number == this->serial_number_pool_.GetCurrentLocation();
}


bool UDPCrediblePatternReceiver::CheckParityCycleTimeContrast(uint32 serial_number, bool even_flag) {
  if(this->CheckSerialNumberOrder(serial_number) == true) {
    return this->serial_number_pool_.CheckEvenLoop() == even_flag;
  } else {
    if(serial_number < this->serial_number_pool_.GetCurrentLocation()) {
      // Current serial number is on the rignt(should not be equal).
      return this->serial_number_pool_.CheckEvenLoop() != even_flag;
    } else {
      // Current serial number is on the left(should be equal).
      return this->serial_number_pool_.CheckEvenLoop() == even_flag;
    }
  }
}

bool UDPCrediblePatternReceiver::InsertSortQueue(uint32 serial_number,
       const char *packet, int size) {
  uint32 *head = (uint32 *)packet;
  if(*head & packet::kCredibleUDPOddEvenFlag) {
    return this->sort_queue_.Write(serial_number, packet, size, false) > 0;
  } else {
    return this->sort_queue_.Write(serial_number, packet, size, true) > 0;
  }
}

int UDPCrediblePatternReceiver::ReceiveFromSortQueue(char *data, int max_size) {
  bool received;
  this->serial_number_pool_.Get(received);
  if(received == false) {
    return -1;
  }
  uint32 serial_number = this->serial_number_pool_.GetCurrentLocation();
  bool odd_even;
  // Read data and remove from sort_queue_.
  int size = this->sort_queue_.Remove(serial_number, data, max_size, odd_even);
  if(size <= 0) {
    return -1;
  }
  // Unpacket data.
  size = this->Unpacket(data, size, data, max_size);
  if(size < 0){
    LOG(ERROR) << "Failed to Unpacket, when ReceiveFromSortQueue!";
    return -1;
  }
  // Set unreceived.
  this->serial_number_pool_.Set(false);
  // Backward serial_number_pool_ pointer.
  this->serial_number_pool_.ShiftBackward();
  return size;
}

int UDPCrediblePatternReceiver::Unpacket(const char *packet, int size,
       char *data, int max_size) {
  int unpacket_size = size - packet::kCredibleUDPHeadSize;
  if(max_size < unpacket_size) {
    return -1;
  }
  memmove(data, packet + packet::kCredibleUDPHeadSize, unpacket_size);
  return unpacket_size;
}

}  // namespace amz


